﻿using System;
using Tesla.Bounding;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Input;
using Tesla.Math;
using Tesla.Scene;
using Tesla.Scene.Shape;

namespace TeslaSamples.Renderer {
    [AppDescription("Render Queue Sample", PlatformCapabilities.Direct3D10 | PlatformCapabilities.XNA4, "RenderQueueImage", "RenderQueueDesc", "RenderQueueSource")]
    public class RenderQueueSample : BasicApp {
        private Node transpNode;
        private Node orthos;
        private ICamera orthoCam;
        private Box opaqueBox1;
        private Box opaqueBox2;
        private Box opaqueBox3;
        private Box transparentBox;
        private Teapot transparentTeapot;
        private Torus transparentTorus;
        private Quad quad1;
        private Quad quad2;
        private bool toggleRenderQueue;
        private bool toggleTwoPass;

        private SpriteBatch batch;
        private SpriteFont font;

        private String text = "Renderqueue: false (R)\nTwo Pass Transparency: false (T)";

        protected override void LoadContent() {
            Window.Title = "Render Queue Sample";
            toggleRenderQueue = false;
            toggleTwoPass = false;

            transpNode = new Node("TransparentNode");
            transpNode.SceneHints.TransparencyType = TransparencyType.OneSided;
            RootNode.AddChild(transpNode);

            orthos = new Node("OrthoNode");
            orthoCam = new Camera(new Viewport());
            orthoCam.SetFrame(new Vector3(-.5f, -.5f, 0), Matrix.Identity);

            quad1 = new Quad("Quad1", 100, 70);
            quad1.Material = ContentManager.Load<Material>("BasicColor.tem").Clone();
            quad1.Material.SetParameter("DiffuseColor", Color.Blue);
            quad1.Translation = new Vector3(Window.ClientBounds.Width / 2, Window.ClientBounds.Height / 2, 0);
            quad1.RenderBucketType = RenderBucketType.Skip;
            quad1.SceneHints.CullHint = CullHint.Never;
            quad1.OrthoOrder = 5;
            quad1.SetRenderState(DepthStencilState.None);
            quad1.SetRenderState(RasterizerState.CullNone);
            orthos.AddChild(quad1);

            quad2 = new Quad("Quad2", 50, 70);
            quad2.Material = ContentManager.Load<Material>("BasicColor.tem").Clone();
            quad2.Material.SetParameter("DiffuseColor", Color.Red);
            quad2.Translation = new Vector3(Window.ClientBounds.Width / 2 + 50, Window.ClientBounds.Height / 2 + 50, 0);
            quad2.RenderBucketType = RenderBucketType.Skip;
            quad2.SceneHints.CullHint = CullHint.Never;
            quad2.OrthoOrder = 1; //Setting the ortho order less than quad1 means this will be drawn first when in the ortho render bucket
            quad2.SetRenderState(DepthStencilState.None);
            quad2.SetRenderState(RasterizerState.CullNone);
            orthos.AddChild(quad2);

            transparentTorus = new Torus("TransparentSphere", 30, 30, 8, 16);
            transparentTorus.Material = ContentManager.Load<Material>("LitBasicColor.tem").Clone();
            transparentTorus.Material.SetParameter("MatDiffuse", Color.Green.ToVector3());
            transparentTorus.Material.SetParameter("Alpha", .75f);
            transparentTorus.SetRenderState(BlendState.AlphaBlendNonPremultiplied);
            transparentTorus.RenderBucketType = RenderBucketType.Skip;
            transparentTorus.Translation = new Vector3(30, 10, 80);
            transpNode.AddChild(transparentTorus);

            transparentBox = new Box("TransparentBox", 10, 10, 10);
            transparentBox.Material = ContentManager.Load<Material>("LitBasicColor.tem").Clone();
            transparentBox.Material.SetParameter("MatDiffuse", Color.DarkRed.ToVector3());
            transparentBox.Material.SetParameter("Alpha", .5f);
            transparentBox.SetRenderState(BlendState.AlphaBlendNonPremultiplied);
            transparentBox.RenderBucketType = RenderBucketType.Skip;
            transparentBox.Translation = new Vector3(0, 10, 50);
            transpNode.AddChild(transparentBox);

            transparentTeapot = new Teapot("TransparentTeapot");
            transparentTeapot.Material = ContentManager.Load<Material>("LitBasicColor.tem").Clone();
            transparentTeapot.Material.SetParameter("MatDiffuse", Color.LightGreen.ToVector3());
            transparentTeapot.Material.SetParameter("Alpha", .35f);
            transparentTeapot.SetRenderState(BlendState.AlphaBlendNonPremultiplied);
            transparentTeapot.RenderBucketType = RenderBucketType.Skip;
            transparentTeapot.Translation = new Vector3(-10, 40, 40);
            transparentTeapot.SetScale(7.0f);
            transpNode.AddChild(transparentTeapot);

            opaqueBox1 = new Box("OpaqueBox", 10, 10, 10);
            opaqueBox1.Material = ContentManager.Load<Material>("LitBasicTexture.tem").Clone();
            opaqueBox1.Material.SetParameter("DiffuseMap", ContentManager.Load<Texture2D>("Textures//bricks_diff.dds"));
            opaqueBox1.RenderBucketType = RenderBucketType.Skip;
            opaqueBox1.Translation = new Vector3(0, -10, -20);
            RootNode.AddChild(opaqueBox1);

            opaqueBox2 = new Box("OpaqueBox", 10, 10, 10);
            opaqueBox2.Material = ContentManager.Load<Material>("LitBasicTexture.tem").Clone();
            opaqueBox2.Material.SetParameter("DiffuseMap", ContentManager.Load<Texture2D>("Textures//bricks_diff.dds"));
            opaqueBox2.RenderBucketType = RenderBucketType.Skip;
            opaqueBox2.Translation = new Vector3(0, 20, -20);
            RootNode.AddChild(opaqueBox2);

            opaqueBox3 = new Box("OpaqueBox", 10, 10, 10);
            opaqueBox3.Material = ContentManager.Load<Material>("LitBasicTexture.tem").Clone();
            opaqueBox3.Material.SetParameter("DiffuseMap", ContentManager.Load<Texture2D>("Textures//bricks_diff.dds"));
            opaqueBox3.RenderBucketType = RenderBucketType.Skip;
            opaqueBox3.Translation = new Vector3(0, 50, -20);
            RootNode.AddChild(opaqueBox3);

            RootNode.SetModelBound(new BoundingBox());

            InputLayer.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.R, false), new InputAction(
                delegate(GameTime time) {
                    toggleRenderQueue = !toggleRenderQueue;
                    Toggle(toggleRenderQueue, toggleTwoPass);
                }
            )));
            InputLayer.RegisterTrigger(new InputTrigger(new KeyPressedCondition(Keys.T, false), new InputAction(
                delegate(GameTime time) {
                    toggleTwoPass = !toggleTwoPass;
                    Toggle(toggleRenderQueue, toggleTwoPass);
                }
            )));

            //Set up some text rendering
            batch = new SpriteBatch();
            font = ContentManager.Load<SpriteFont>("Fonts//comicsans.fnt");
        }

        protected override void OnUpdate(GameTime time) {
            //Make sure we update the ortho quads
            orthos.Update(time);
        }

        protected override void OnRenderPre(IRenderer renderer) {
            //This is bit of a hack - the ortho quads are not actually part of the regular scene graph,
            //instead we render them before we process the scene graph for drawing.
            //
            //When we draw without the render queue we manually set an ortho camera so the quads are still
            //drawn orthographically. When we use the render queue, we draw the subtree normally and let the
            //render queue pick them up, so drawing them first will mean nothing.
            if(!toggleRenderQueue) {
                Viewport viewport = Renderer.CurrentCamera.Viewport;
                orthoCam.Viewport = viewport;
                orthoCam.SetProjection(ProjectionMode.Orthographic, 0, viewport.Width, viewport.Height, 0, 0, 1);
                orthoCam.Update();
                ICamera oldCam = renderer.CurrentCamera;
                renderer.CurrentCamera = orthoCam;
                orthos.Draw(renderer);
                renderer.CurrentCamera = oldCam;
            } else {
                orthos.Draw(renderer);
            }
        }

        protected override void OnRenderPost(IRenderer renderer) {
            batch.Begin();
            Viewport vp = renderer.CurrentCamera.Viewport;
            Vector2 pos = new Vector2(10, vp.Height - (font.MeasureString(text).Y + 10));
            batch.DrawString(font, text, pos, Color.White);
            batch.End();
        }

        private void Toggle(bool renderQueue, bool twoPass) {
            //Here we toggle the render bucket types for the geometries and the sort of
            //transparent rendering when we use the render queue.
            //
            //Note that when setting the transparency type, we use a quicker way
            //than setting it to each transparent object - since all the transparent
            //meshes are attached to a single node, we can set the scene hints of that
            //node. By default all scene objects inherit their render bucket type
            //and transparency type. The same goes for most of the scene hint enumerations.
            //
            //Therefore, an object that inherits will look at their parent and their parent's parent,
            //and so on to find what hint to use - if all are inherit or no parent, the default is used.
            if(renderQueue) {
                opaqueBox1.RenderBucketType = RenderBucketType.Opaque;
                opaqueBox2.RenderBucketType = RenderBucketType.Opaque;
                opaqueBox3.RenderBucketType = RenderBucketType.Opaque;
                transparentBox.RenderBucketType = RenderBucketType.Transparent;
                transparentTeapot.RenderBucketType = RenderBucketType.Transparent;
                transparentTorus.RenderBucketType = RenderBucketType.Transparent;
                quad1.RenderBucketType = RenderBucketType.Ortho;
                quad2.RenderBucketType = RenderBucketType.Ortho;
            } else {
                opaqueBox1.RenderBucketType = RenderBucketType.Skip;
                opaqueBox2.RenderBucketType = RenderBucketType.Skip;
                opaqueBox3.RenderBucketType = RenderBucketType.Skip;
                transparentBox.RenderBucketType = RenderBucketType.Skip;
                transparentTeapot.RenderBucketType = RenderBucketType.Skip;
                transparentTorus.RenderBucketType = RenderBucketType.Skip;
                quad1.RenderBucketType = RenderBucketType.Skip;
                quad2.RenderBucketType = RenderBucketType.Skip;
            }

            if(twoPass) {
                //This easily be:
                //transparentBox.TransparencyType = TransparencyType.TwoSided
                transpNode.SceneHints.TransparencyType = TransparencyType.TwoSided;
            } else {
                transpNode.SceneHints.TransparencyType = TransparencyType.OneSided;
            }

            text = String.Format("Renderqueue: {0} (R)\nTwo Pass Transparency: {1} (T)", renderQueue.ToString(), twoPass.ToString());
        }
    }
}
